#include "environment.h"
#include "data/bootstrap.h"


/** @brief Default context.
 *
 * The actual default context IRI used throughout the application may use a
 * value from the `LSUP_DEFAULT_CTX_URI` environment variable.
 */
#define DEFAULT_CTX_LABEL LSUP_NS "default"


/*
 * External variables.
 */
LSUP_NSMap *LSUP_default_nsm = NULL;
LSUP_Term *LSUP_default_ctx = NULL;
LSUP_Buffer *LSUP_default_ctx_buf = NULL;


/*
 * Hashmap callbacks.
 */

static uint64_t
term_cache_hash_fn (
        const void *item, uint64_t seed0, uint64_t seed1)
{ return ((const LSUP_KeyedTerm *) item)->key; }


static int
term_cache_cmp_fn (const void *a, const void *b, void *udata)
{
    return 
            ((const LSUP_KeyedTerm *) a)->key -
            ((const LSUP_KeyedTerm *) b)->key;
}


static void
term_cache_free_fn (void *item)
{ LSUP_term_free (((LSUP_KeyedTerm *) item)->term); }


/*
 * API
 */

LSUP_rc
LSUP_init (void)
{
    if (LSUP_IS_INIT) {
        LOG_RC (LSUP_NOACTION);
        return LSUP_NOACTION;
    }

#ifdef DEBUG
    // In debug mode, always use max logging.
    int loglevel = LOG_TRACE;
#else
    char *_loglevel = getenv ("LSUP_LOGLEVEL");
    int loglevel = (_loglevel == NULL) ? LOG_INFO : atoi (_loglevel);
#endif
    log_set_level (loglevel);

    // URI validation pattern.
    MALLOC_GUARD (LSUP_uri_ptn, LSUP_MEM_ERR);

#if 0 // Re-activate in case a change in the URI regex results in an error.
    int regex_rc = regcomp (LSUP_uri_ptn, LSUP_URI_REGEX_STR, REG_EXTENDED);
    if (regex_rc != 0) {
        char err_msg[128];
        size_t err_msg_sz = regerror (regex_rc, LSUP_uri_ptn, err_msg, 128);
        log_error (
                "Error compiling regular expression pattern: %s.",
                err_msg);
        return LSUP_ERROR;
    }
#else
    if (regcomp (LSUP_uri_ptn, LSUP_URI_REGEX_STR, REG_EXTENDED) != 0) {
        log_error ("Error compiling regular expression pattern.");
        return LSUP_ERROR;
    }
#endif

    // Default namespace map.
    LSUP_default_nsm = LSUP_nsmap_new();
    if (UNLIKELY (!LSUP_default_nsm)) return LSUP_ERROR;
    for (int i = 0; init_nsmap[i][0] != NULL; i++)
        LSUP_nsmap_add (LSUP_default_nsm, init_nsmap[i][0], init_nsmap[i][1]);

    // Default context URI.
    char *default_ctx_str = getenv ("LSUP_DEFAULT_CTX");
    if (!default_ctx_str ) default_ctx_str = DEFAULT_CTX_LABEL;
    LSUP_default_ctx = LSUP_iriref_new (default_ctx_str, LSUP_default_nsm);
    if (UNLIKELY (!LSUP_default_ctx)) return LSUP_ERROR;
    LSUP_default_ctx_buf = LSUP_term_serialize (LSUP_default_ctx);
    if (UNLIKELY (!LSUP_default_ctx_buf)) return LSUP_ERROR;

    // Initialize term cache.
    LSUP_term_cache = hashmap_new (
            sizeof(LSUP_KeyedTerm), 0, LSUP_HASH_SEED, 0,
            term_cache_hash_fn, term_cache_cmp_fn, term_cache_free_fn, NULL);
    // Create and cache default literal datatype key.
    LSUP_default_datatype = LSUP_iriref_new (DEFAULT_DTYPE, NULL);
    uint32_t dtype_hash = LSUP_term_hash (LSUP_default_datatype );
    LSUP_tcache_add (dtype_hash, LSUP_default_datatype);

    // Set automatic teardown TODO Is this a good idea?
    atexit (LSUP_done);

    return LSUP_OK;
}


void
LSUP_done (void)
{
    if (!LSUP_IS_INIT) return;

    regfree (LSUP_uri_ptn);
    free (LSUP_uri_ptn);

    // Free default NS map and context.
    LSUP_buffer_free (LSUP_default_ctx_buf);
    LSUP_term_free (LSUP_default_ctx);
    LSUP_nsmap_free (LSUP_default_nsm);

    // Free ID cache, including default literal datatype.
    hashmap_free (LSUP_term_cache);
    LSUP_term_cache = NULL; // This causes LSUP_IS_INIT to return false.
}


/*
 * Extern inline prototypes.
 */

bool LSUP_is_init (void);
